// 核心原则
// 当使用new和delete时，必须确保它们的形式相匹配
// 如果使用new 必须使用delete
// 如果使用new[] 必须使用delete[]

// 为什么这很重要
// 这个规则背后是内存管理机制：
// 单个对象的内存布局
// 当使用普通new分配单个对象时：
MyClass *ptr = new MyClass();
// 内存布局大概为
| 对象数据 |
// 系统只需要知道对象的大小，就能正确分配和释放内存。

// 数组的内存布局
// 当使用new[]分配对象数组时：
MyClass *arr = new MyClass[10];
| 数组大小信息 | 对象1 | 对象2 | ... | 对象10 |
// 系统需要记录数组的大小，以便能够正确调用每个对象的析构函数。

// 错误示例及后果
// 错误1：对数组使用普通delete
int *arr = new int[10];
// ...使用数组...
delete arr; // 错误！应该使用delete[]

// 后果：
// 对于基本类型（如int），可能不会立即出现问题
// 但这是未定义行为，可能导致内存泄漏或其他问题

// 错误2：对单个对象使用delete[]
MyClass *obj = new MyClass();
// ...使用对象...
delete[] obj; // 错误！应该使用delete
// 后果：
// 系统会尝试读取不存在的数组大小信息
// 可能导致内存损坏、程序崩溃或其他未定义行为

// 错误3：对复杂对象数组使用普通delete
class MyClass
{
public:
    ~MyClass() { /* 清理资源 */ }
    // ...其他成员...
};

MyClass *arr = new MyClass[10];
// ...使用数组...
delete arr; // 错误！只会调用第一个对象的析构函数
// 后果：
// 只有数组中的第一个对象会调用析构函数
// 其余9个对象的析构函数不会被调用，导致资源泄漏

// 避免问题的最佳实践
// 1. 使用智能指针 智能指针会自动使用正确的删除形式。
// 单个对象
std::unique_ptr<MyClass> obj = std::make_unique<MyClass>();

// 数组（C++14及以后）
// 数组智能指针std::unique_ptr<T[]>，创建一个包含 10个 MyClass 对象的数组

std::unique_ptr<MyClass[]> arr = std::make_unique<MyClass[]>(10);
arr[0].someMethod(); // 可以使用[]访问元素
MyClass &ref = *arr; // 错误！不能直接解引用
arr->someMethod();   // 错误！不能使用->

// 2. 使用标准容器
// 使用vector代替原始数组 标准容器会自动管理内存，无需手动调用delete。
std::vector<MyClass> arr(10);

// 3. 保持一致的分配/释放模式
// 如果必须使用原始的new/delete，确保它们成对出现：
// 在同一个作用域内保持分配和释放的代码接近
MyClass *obj = new MyClass();
// ...使用对象...
delete obj;

MyClass *arr = new MyClass[10];
// ...使用数组...
delete[] arr;

// 示例：自定义类数组
class Resource
{
public:
    Resource() { std::cout << "Resource acquired\n"; }
    ~Resource() { std::cout << "Resource released\n"; }
};

// 错误的做法
Resource *resources = new Resource[5];
delete resources; // 只会调用一次析构函数！

// 正确的做法
Resource *resources = new Resource[5];
delete[] resources; // 会调用5次析构函数

// 最佳做法
std::vector<Resource> resources(5); // 自动管理内存

// 总结
// 基本规则：new配对delete，new[]配对delete[]
// 违反规则的后果：未定义行为、内存泄漏、资源未释放、程序崩溃
// 最佳实践：
// 尽量使用智能指针和标准容器
// 如果必须使用原始指针，确保分配和释放形式匹配
// 保持分配和释放代码在同一作用域内且靠近
// 记忆技巧：有方括号的一定要配对使用方括号
// 遵循这个条款可以避免许多常见的内存管理错误，提高代码的健壮性和可靠性。